import { Processor, Process, OnQueueActive, OnQueueCompleted, OnQueueFailed } from '@nestjs/bull';
import { Job } from 'bullmq';
import { Logger, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Inject } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';

// 服务
import { AiInferenceService } from './ai-inference.service';
import { PrismaService } from '../../database/prisma.service';

// 异常
import {
  AiInferenceException,
  DatabaseException,
  QueueException,
  ExceptionUtils,
  ErrorCode,
} from './exceptions/scoring.exceptions';

// 类型定义
interface AnalyzeImageJobData {
  taskId: number;
  filePath: string;
  correlationId: string;
  userId: number;
}

/**
 * 评分任务队列处理器
 * 零隐患MFIS规范 - 解决隐患：#16 并发控制，#3 AI服务熔断，#9 数据库事务
 */
@Injectable()
@Processor('file-processing')
export class ScoringProcessor {
  private readonly logger = new Logger(ScoringProcessor.name);

  constructor(
    private readonly aiInferenceService: AiInferenceService,
    private readonly prisma: PrismaService,
    private readonly configService: ConfigService,
    @Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
  ) {
    this.logger.log('🔧 ScoringProcessor 已初始化 - 零隐患MFIS规范');
  }

  /**
   * 处理图像分析任务
   * 解决隐患：#3 AI服务熔断，#9 数据库事务处理，#14 日志信息泄露
   */
  @Process('analyze-image')
  async handleAnalyzeImage(job: Job<AnalyzeImageJobData>): Promise<void> {
    const { taskId, filePath, correlationId, userId } = job.data;
    const startTime = Date.now();

    this.logger.log(`Job ${job.id} 开始处理任务 ${taskId}`, {
      task_id: taskId,
      correlation_id: correlationId,
      user_id: userId,
      file_path: this.sanitizePath(filePath),
      job_attempts: job.attemptsMade + 1,
    });

    try {
      // 1. 更新任务状态为ANALYZING
      await this.updateTaskStatus(taskId, {
        status: 'ANALYZING',
        start_time: new Date(),
      });

      // 2. 记录队列任务状态
      await this.recordQueueJob(job, 'active', startTime);

      // 3. 执行AI分析（熔断器保护）
      const aiResult = await this.performAiAnalysis(filePath, correlationId);

      // 4. 更新任务结果
      const processingDuration = Date.now() - startTime;
      await this.updateTaskResult(taskId, {
        status: 'COMPLETED',
        complete_time: new Date(),
        score: aiResult.score,
        severity: aiResult.severity,
        confidence: aiResult.confidence,
        ai_analysis_data: aiResult,
        processing_duration_ms: processingDuration,
      });

      // 5. 清理缓存
      await this.clearTaskCache(taskId);

      // 6. 记录成功审计日志
      await this.recordAuditLog(userId, 'TASK_COMPLETED', 'scoring_task', taskId.toString(), {
        score: aiResult.score,
        confidence: aiResult.confidence,
        processing_duration_ms: processingDuration,
        correlation_id: correlationId,
      });

      this.logger.log(`Job ${job.id} 处理成功`, {
        task_id: taskId,
        score: aiResult.score,
        confidence: aiResult.confidence,
        inference_time: aiResult.inference_time,
        total_processing_time: processingDuration,
        correlation_id: correlationId,
      });

    } catch (error) {
      await this.handleAnalysisError(job, taskId, error, correlationId, startTime);
    }
  }

  /**
   * 执行AI分析
   * 解决隐患：#3 AI服务熔断
   */
  private async performAiAnalysis(filePath: string, correlationId: string): Promise<any> {
    try {
      this.logger.debug('开始AI推理', {
        correlation_id: correlationId,
        file_path: this.sanitizePath(filePath),
      });

      const aiResult = await this.aiInferenceService.callInference(filePath);

      // 验证AI结果格式
      this.validateAiResult(aiResult);

      return aiResult;

    } catch (error) {
      this.logger.error('AI推理失败', {
        correlation_id: correlationId,
        error: ExceptionUtils.createUserFriendlyResponse(error),
      });

      if (error instanceof AiInferenceException) {
        throw error;
      }

      throw new AiInferenceException('AI分析失败', error);
    }
  }

  /**
   * 验证AI结果格式
   */
  private validateAiResult(result: any): void {
    const requiredFields = ['score', 'severity', 'confidence', 'model_version'];

    for (const field of requiredFields) {
      if (!(field in result)) {
        throw new AiInferenceException(`AI结果缺少必需字段: ${field}`);
      }
    }

    // 验证数据范围
    if (typeof result.score !== 'number' || result.score < 0 || result.score > 8) {
      throw new AiInferenceException('AI评分值无效');
    }

    if (typeof result.confidence !== 'number' || result.confidence < 0 || result.confidence > 1) {
      throw new AiInferenceException('AI置信度值无效');
    }
  }

  /**
   * 处理分析错误
   * 解决隐患：#9 数据库事务，#14 日志信息泄露
   */
  private async handleAnalysisError(
    job: Job<AnalyzeImageJobData>,
    taskId: number,
    error: Error,
    correlationId: string,
    startTime: number,
  ): Promise<void> {
    const processingDuration = Date.now() - startTime;

    // 脱敏错误消息
    const sanitizedError = ExceptionUtils.createUserFriendlyResponse(error);

    this.logger.error(`Job ${job.id} 处理失败`, {
      task_id: taskId,
      correlation_id: correlationId,
      error: sanitizedError,
      error_type: error.constructor.name,
      processing_duration_ms: processingDuration,
      job_attempts: job.attemptsMade + 1,
      max_attempts: job.opts.attempts || 3,
    });

    try {
      // 更新任务状态为FAILED
      await this.updateTaskStatus(taskId, {
        status: 'FAILED',
        complete_time: new Date(),
        error_message: this.sanitizeErrorMessage(error.message),
        retry_count: job.attemptsMade,
        processing_duration_ms: processingDuration,
      });

      // 记录失败审计日志
      await this.recordAuditLog(job.data.userId, 'TASK_FAILED', 'scoring_task', taskId.toString(), {
        error_type: error.constructor.name,
        correlation_id: correlationId,
        processing_duration_ms: processingDuration,
      });

      // 清理缓存
      await this.clearTaskCache(taskId);

    } catch (updateError) {
      this.logger.error('更新失败任务状态时发生错误', {
        task_id: taskId,
        update_error: updateError.message,
      });
    }

    // 记录队列任务失败状态
    await this.recordQueueJob(job, 'failed', startTime, error.message);

    // 根据错误类型决定是否重试
    if (ExceptionUtils.isRetryableError(error)) {
      this.logger.info('任务将自动重试', {
        task_id: taskId,
        attempts: job.attemptsMade + 1,
      });
    } else {
      this.logger.warn('任务不可重试，将标记为最终失败', {
        task_id: taskId,
      });
    }
  }

  /**
   * 更新任务状态
   * 解决隐患：#9 数据库事务处理
   */
  private async updateTaskStatus(taskId: number, updateData: any): Promise<void> {
    try {
      await this.prisma.scoringTask.update({
        where: { task_id: taskId },
        data: {
          ...updateData,
          updated_at: new Date(),
        },
      });
    } catch (error) {
      throw new DatabaseException('更新任务状态失败', error);
    }
  }

  /**
   * 更新任务结果
   */
  private async updateTaskResult(taskId: number, resultData: any): Promise<void> {
    try {
      await this.prisma.scoringTask.update({
        where: { task_id: taskId },
        data: {
          ...resultData,
          updated_at: new Date(),
        },
      });
    } catch (error) {
      throw new DatabaseException('更新任务结果失败', error);
    }
  }

  /**
   * 记录审计日志
   */
  private async recordAuditLog(
    userId: number,
    action: string,
    resourceType: string,
    resourceId: string,
    metadata: any,
  ): Promise<void> {
    try {
      await this.prisma.auditLog.create({
        data: {
          user_id: userId,
          user_type: 'system',
          action,
          resource_type: resourceType,
          resource_id: resourceId,
          request_data: metadata,
          severity: 'INFO',
          correlation_id: metadata.correlation_id,
          created_at: new Date(),
        },
      });
    } catch (error) {
      // 审计日志失败不应该影响主流程
      this.logger.warn('记录审计日志失败', {
        error: error.message,
        action,
        resource_id: resourceId,
      });
    }
  }

  /**
   * 记录队列任务状态
   */
  private async recordQueueJob(
    job: Job,
    status: string,
    startTime: number,
    errorMessage?: string,
  ): Promise<void> {
    try {
      const jobData = {
        job_id: job.id.toString(),
        queue_name: job.queueName,
        task_id: job.data.taskId,
        job_data: job.data,
        status,
        priority: job.opts.priority || 5,
        attempts: job.attemptsMade,
        max_attempts: job.opts.attempts || 3,
        created_at: new Date(job.timestamp),
        started_at: status === 'active' ? new Date(startTime) : undefined,
        completed_at: status === 'completed' ? new Date() : undefined,
        failed_at: status === 'failed' ? new Date() : undefined,
        error_message: errorMessage ? this.sanitizeErrorMessage(errorMessage) : undefined,
      };

      await this.prisma.queueJob.create({ data: jobData });

    } catch (error) {
      this.logger.warn('记录队列任务状态失败', {
        job_id: job.id,
        error: error.message,
      });
    }
  }

  /**
   * 清理任务缓存
   */
  private async clearTaskCache(taskId: number): Promise<void> {
    try {
      await this.cacheManager.del(`task_status:${taskId}`);
    } catch (error) {
      this.logger.warn('清理缓存失败', {
        task_id: taskId,
        error: error.message,
      });
    }
  }

  /**
   * 路径脱敏
   * 解决隐患：#14 日志信息泄露
   */
  private sanitizePath(filePath: string): string {
    if (!filePath) return '';

    return filePath
      .replace(/[A-Za-z]:\\/g, '[驱动器]:\\')
      .replace(/\\/g, '/')
      .replace(/\/[^\/]+\/[^\/]+\/([^\/]+)$/, '/.../$1');
  }

  /**
   * 错误消息脱敏
   */
  private sanitizeErrorMessage(message: string): string {
    return message
      .replace(/password[=:]\s*\S+/gi, 'password=***')
      .replace(/token[=:]\s*\S+/gi, 'token=***')
      .replace(/key[=:]\s*\S+/gi, 'key=***')
      .substring(0, 200);
  }

  // 队列事件监听器

  @OnQueueActive()
  onActive(job: Job) {
    this.logger.debug(`Job ${job.id} 开始执行`, {
      job_name: job.name,
      task_id: job.data.taskId,
    });
  }

  @OnQueueCompleted()
  onCompleted(job: Job, result: any) {
    this.logger.log(`Job ${job.id} 执行完成`, {
      job_name: job.name,
      task_id: job.data.taskId,
      correlation_id: job.data.correlationId,
    });
  }

  @OnQueueFailed()
  onFailed(job: Job, error: Error) {
    this.logger.error(`Job ${job.id} 执行失败`, {
      job_name: job.name,
      task_id: job.data.taskId,
      correlation_id: job.data.correlationId,
      error: this.sanitizeErrorMessage(error.message),
      attempts: job.attemptsMade + 1,
    });
  }
}